﻿using Adnc.Application.Shared;
using Adnc.Application.Shared.Dtos;
using Adnc.Application.Shared.Enum;
using Adnc.Application.Shared.Services;
using Adnc.Infra.EventBus;
using Adnc.Infra.Helper;
using Adnc.Infra.IRepositories;
using Adnc.Infra.Repository.Entities.MongoEntities;
using Adnc.Shared.Events;
using Adnc.Shared.RpcServices.Rtos.Pro;
using Adnc.Shared.RpcServices.Rtos.Usr;
using Adnc.Shared.RpcServices.Services;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using WYT.Ord.Application.Contracts.Dtos.Coupon;
using WYT.Ord.Application.Contracts.Dtos.Order;
using WYT.Ord.Application.Contracts.Services;
using WYT.Ord.Repository.Entities;

namespace WYT.Ord.Application.Services
{
    /// <summary>
    /// 订单服务
    /// </summary>
    public class OrderAppService : AbstractAppService, IOrderAppService
    {
        private readonly IEfRepository<Order> _orderRepository;

        private readonly IEfRepository<OrderGoods> _orderGoodsRepository;

        private readonly IEfRepository<ShoppingCart> _shoppingCartRepository;

        private readonly IEfRepository<OrderParcel> _orderParcelRepository;

        private readonly IEfRepository<OrderParcelGoods> _orderParcelGoodsRepository;

        private readonly IEfRepository<OrderCoupon> _orderCouponRepository;

        private readonly IEfRepository<UserCoupon> _userCouponRepository;

        private readonly IEfRepository<Coupon> _couponRepository;

        private readonly IProductRPCService _productRPCService;

        private readonly IUserRpcService _userRpcService;

        private readonly IPayRpcService _payRpcService;

        private readonly IEventPublisher _eventPublisher;

        private readonly IMongoRepository<MessageLog> _messageLogRepository;

        private readonly IOptions<ERPOptions> _erpOptions;

        //代金券ID
        private readonly long CashCoupon = 2;

        //体验装优惠券
        private readonly long ExperienceCoupon = 1;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="orderRepository"></param>
        /// <param name="orderGoodsRepository"></param>
        /// <param name="shoppingCartRepository"></param>
        /// <param name="orderParcelRepository"></param>
        /// <param name="orderParcelGoodsRepository"></param>
        /// <param name="userCouponRepository"></param>
        /// <param name="couponRepository"></param>
        /// <param name="orderCouponRepository"></param>
        /// <param name="messageLogRepository"></param>
        /// <param name="productRPCService"></param>
        /// <param name="userRpcService"></param>
        /// <param name="payRpcService"></param>
        /// <param name="eventPublisher"></param>
        /// <param name="erpOptions"></param>
        public OrderAppService(IEfRepository<Order> orderRepository,
            IEfRepository<OrderGoods> orderGoodsRepository,
            IEfRepository<ShoppingCart> shoppingCartRepository,
            IEfRepository<OrderParcel> orderParcelRepository,
            IEfRepository<OrderParcelGoods> orderParcelGoodsRepository,
            IEfRepository<UserCoupon> userCouponRepository,
            IEfRepository<Coupon> couponRepository,
            IEfRepository<OrderCoupon> orderCouponRepository,
            IMongoRepository<MessageLog> messageLogRepository,
            IProductRPCService productRPCService,
            IUserRpcService userRpcService,
            IPayRpcService payRpcService,
            IEventPublisher eventPublisher,
            IOptions<ERPOptions> erpOptions)
        {
            _orderRepository = orderRepository;
            _orderGoodsRepository = orderGoodsRepository;
            _shoppingCartRepository = shoppingCartRepository;
            _productRPCService = productRPCService;
            _orderParcelRepository = orderParcelRepository;
            _orderParcelGoodsRepository = orderParcelGoodsRepository;
            _userCouponRepository = userCouponRepository;
            _messageLogRepository = messageLogRepository;
            _couponRepository = couponRepository;
            _orderCouponRepository = orderCouponRepository;
            _userRpcService = userRpcService;
            _eventPublisher = eventPublisher;
            _payRpcService = payRpcService;
            _erpOptions = erpOptions;
        }

        /// <summary>
        /// 创建订单
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<AppSrvResult<long>> OrderCreate(long userid, OrderCreationDto input)
        {
            var userinforResponse = await _userRpcService.GetUserInfor();
            if (!userinforResponse.IsSuccessStatusCode)
                return Problem(HttpStatusCode.InternalServerError, "服务器异常");
            var userinfor = userinforResponse.Content;

            if (string.IsNullOrEmpty(userinfor.UnionID))
                return Problem(null, "请先完善个人信息");
            if (userinfor.State == 2)
                return Problem(null, "你的账号处于冻结状态,不能下单，如果有疑问请联系客服");
            if (userinfor.State == 1 && (input.PayMode == EnumPayMode.BALANCE || input.PayMode == EnumPayMode.BONUS))
                return Problem(null, "你的钱包处于冻结状态,只能使用微信支付,如果有疑问请联系客服");

            #region 交易密码检查
            if (input.PayMode == EnumPayMode.BONUS || input.PayMode == EnumPayMode.BALANCE)
            {
                if(string.IsNullOrWhiteSpace(input.Pass))
                    return Problem(HttpStatusCode.BadRequest, "交易密码不能为空");

                if (string.IsNullOrWhiteSpace(userinfor.WithdrawalPassword))
                    return Problem(HttpStatusCode.BadRequest, "请设置交易密码");

                if(!userinfor.WithdrawalPassword.Equals(HashHelper.GetHashedString(HashType.MD5, input.Pass)))
                    return Problem(HttpStatusCode.BadRequest, "支付密码错误");
            }
            #endregion

            #region 订单检查
            if (input.Type == 2)
            {
                if (input.PayMode == EnumPayMode.BONUS)
                    return Problem(null, "不能使用购物金支付");
                if (input.Products.Count != 1)
                    return Problem(null, "一次只能兑换一个体验装");
                if (input.CouponIds.Count != 1)
                    return Problem(HttpStatusCode.InternalServerError, "您不能购买体验装，请使用免费领取功能领取");
            }
            else if (input.Type == 5)
            {
                var count = input.Products.Sum(x => x.Count);
                if(count < 5)
                    return Problem(null, "通排调补订单每次做少需要5件商品才可以下单");
            }
            #endregion

            var sizeIds = input.Products.Select(c => c.SizeId).ToArray();

            //根据规格获取商品信息
            var proResponse = await _productRPCService.GetProductSizeDetailByIds(sizeIds,true);
            if (!proResponse.IsSuccessStatusCode)
                return Problem(HttpStatusCode.InternalServerError, "服务器异常,获取商品信息失败");
            var orderProduct = proResponse.Content;

            #region 数据验证
            UserAddressRto? userAddress = null;
            //除云仓订单外，需要根据收货地址判断商品是否可售、地址是否完整
            if (input.Type != 3)
            {
                //获取收货地址
                var userAddrResponse = await _userRpcService.GetAddressDetail(input.AddressId);
                if (!userAddrResponse.IsSuccessStatusCode)
                {
                    if (userAddrResponse.StatusCode == HttpStatusCode.NotFound)
                        return Problem(HttpStatusCode.NotFound, "用户收货地址错误");
                    else
                        return Problem(HttpStatusCode.InternalServerError, "服务器异常,获取用户地址信息失败");
                }
                    
                userAddress = userAddrResponse.Content;

                //判断收货地址是否可售
                string addressCode = userAddress.AreaCode;
                foreach (var item in orderProduct)
                {
                    var areas = JsonSerializer.Deserialize<string[]>(item.NotSellAreaCode);
                    foreach (var area in areas)
                    {
                        var areacode = area.TrimEnd('0');
                        if (areacode == addressCode[..areacode.Length])
                            return Problem(HttpStatusCode.BadRequest, "商品不在可售范围内,请与客服联系！");
                    }
                }

                //是国外商品时需要用户信息
                if (orderProduct.Any(c => c.IsForeign == true) && (string.IsNullOrEmpty(userAddress.IDCard) || string.IsNullOrEmpty(userAddress.Compellation)))
                    return Problem(HttpStatusCode.BadRequest, "请完善身份证信息");
            }

            //如何是自提单需要判断用户云仓中的商品是否足够
            if (input.Type == 4)
            {
                var userWarehouseResponse = await _userRpcService.GetRPCUserWarehouse();
                if (!userWarehouseResponse.IsSuccessStatusCode)
                    return Problem(HttpStatusCode.InternalServerError, "服务器异常,获取用户库存信息失败");
                var warehouse = userWarehouseResponse.Content;

                //自提数量 按照类型统计
                var pq = (from d in orderProduct
                join p in input.Products on d.SizeId equals p.SizeId
                group new { d, p } by new { d.GoodsType } into g
                select new
                {
                    g.Key.GoodsType,
                    count = g.Sum(x => x.p.Count)
                }).ToList();

                //云仓数量  按照商品类型统计
                var wq = warehouse.GroupBy(x => x.GoodsType).Select(x => new { GoodsType = x.Key, count = x.Sum(c => c.Quantity)}).ToList();

                foreach (var item in pq)
                {
                    var count = (wq.FirstOrDefault(x => x.GoodsType == item.GoodsType)?.count) ?? 0;
                    if (item.count > count)
                        return Problem(null, "云仓中的商品数量不足");
                }
            }

            //判断商品库存是否足够
            foreach (var item in input.Products)
            {
                var pro = orderProduct.First(x => x.SizeId == item.SizeId);
                if (pro.Inventory < item.Count)
                    return Problem(null, $"非常抱歉，{pro.GoodsName}({pro.SizeName})的库存已不足！");
            }
            #endregion

            //计算商品价格

            var orderGoods = (from pd in orderProduct
                            join p in input.Products on pd.SizeId equals p.SizeId
                            select new OrderProductInforDto
                            {
                                GoodsId = pd.GoodsId,
                                SizeId = pd.SizeId,
                                Count = p.Count,
                                Price = pd.ProductPrices.First(x => x.UserLevel == userinfor.Grade).Price,
                                IsThirdParties = pd.IsThirdParties,
                            }).ToList();
            if(input.Type == 4)
                orderGoods.ForEach(x => x.Price = 0);
            else
                orderGoods.ForEach(x => x.Total = x.Price * x.Count);

            decimal total = orderGoods.Sum(c => c.Total);
            //优惠金额
            decimal discount = 0;

            //使用抵扣券的商品
            long DeductedGoods = 0;
            //计算优惠券
            if (input.CouponIds.Count > 0)
            {
                var usrCoupons = await _userCouponRepository.Where(x => input.CouponIds.Contains(x.Id)).ToListAsync();
                var couponIds = usrCoupons.Select(x => x.CouponId).ToList();
                var coupons = await _couponRepository.Where(x => couponIds.Contains(x.Id)).ToListAsync();

                var couponinfor =  (from uc in usrCoupons
                                    join c in coupons on uc.CouponId equals c.Id
                                    select new CouponDto
                                    {
                                        Id = uc.Id,
                                        CouponId = c.Id,
                                        CouponName = c.Name,
                                        Amount = c.Amount,
                                        CouponType = c.Type,
                                        GoodsAstrict = c.GoodsAstrict,
                                        IsSuperposition = c.IsSuperposition,
                                        State = uc.State,
                                        StartTime = uc.StartTime,
                                        EndTime = uc.EndTime,
                                        ReachAmount = c.ReachAmount,
                                        MaxAmount = c.MaxAmount,
                                        AvailableGoods = c.AvailableGoods.ToLongArray(",", StringSplitOptions.RemoveEmptyEntries)
                                    }).ToList();

                if (couponinfor.Any(x => x.State != 1 || x.EndTime < DateTime.Now || x.StartTime > DateTime.Now))
                    return Problem(null, "优惠券已过期或者重复使用！");

                var og = orderGoods.FindAll(x => true);

                //抵扣券
                var dk = couponinfor.FindAll(x => x.CouponType == (int)EnumCouponType.DIKOU);
                if (dk.Count() > 0)
                {
                    //一个订单最多使用一张抵扣券
                    if (dk.Count() > 1)
                        return Problem(null, "优惠券使用错误");
                    else
                    {
                        var fg = og.FindAll(x => dk.First().AvailableGoods.Contains(x.GoodsId)).OrderBy(x => x.Price);
                        if(fg.Count() == 0)
                            return Problem(null, "优惠券使用错误");
                        var dkg = fg.OrderBy(x => x.Price).First();
                        discount += dkg.Price;
                        DeductedGoods = dkg.SizeId;
                        og.RemoveAll(x => x.GoodsId == dkg.GoodsId);
                    }
                }

                var ndc = couponinfor.FindAll(x => x.CouponType != (int)EnumCouponType.DIKOU);
                if (ndc.Count() > 0)
                {
                    var fq = ndc.First();
                    if(ndc.Count() > 1)
                    {
                        if (ndc.Any(x => x.CouponType != fq.CouponType || x.GoodsAstrict != fq.GoodsAstrict || x.IsSuperposition == false))
                            return Problem(null, "优惠券使用错误");
                        if (fq.GoodsAstrict == 2)
                        {
                            for (var i = 1; i < ndc.Count(); i++)
                            {
                                if (ndc[i].AvailableGoods.Count() != fq.AvailableGoods.Count() || fq.AvailableGoods.Concat(ndc[i].AvailableGoods).Distinct().Count() != fq.AvailableGoods.Count())
                                    return Problem(null, "优惠券使用错误");
                            }
                        }
                    }

                    if(fq.GoodsAstrict == 2)
                        og = og.FindAll(x =>fq.AvailableGoods.Contains(x.GoodsId)).ToList();
                    else if(fq.GoodsAstrict == 3)
                        og = og.FindAll(x => x.IsThirdParties).ToList();
                    else if(fq.GoodsAstrict == 4)
                        og = og.FindAll(x => x.IsThirdParties == false).ToList();
                    //判断是否达到使用条件
                    var amount1 = og.Sum(x => x.Total);
                    var amount2 = ndc.Sum(x => x.ReachAmount);
                    if (amount1 < amount2)
                        return Problem(null, "优惠券使用错误");
                    if(fq.CouponType == 2)
                        discount += ndc.Sum(x => x.Amount);
                    else if(fq.CouponType == 3)
                    {
                        var dis = (10 - fq.Amount) * amount1 / 10;
                        dis = dis <=  fq.MaxAmount ? dis : fq.MaxAmount;
                        discount += dis;
                    }
                }

                await _userCouponRepository.UpdateRangeAsync(x => couponIds.Contains(x.Id), x => new UserCoupon() { State = 2 });
            }

            if(total < discount)
                discount = total;

            //计算邮费
            decimal postage = 0;
            if(input.Type != 3)
            {
                var product = orderGoods.GroupBy(x => x.GoodsId).Select(x => new ProductInforRto { Id = x.Key, Count = x.Sum(c => c.Count), Amount = x.Sum(c => c.Total) }).ToList();
                var postageResponse = await _productRPCService.PostageCalculation(new PostageCalculationRto { AddrCode = userAddress.AreaCode, ProductInfors = product });
                if (!postageResponse.IsSuccessStatusCode)
                    return Problem(HttpStatusCode.InternalServerError, "计算邮费错误");
                postage = (postageResponse.Content?.TotalPostage) ?? 0;
            }

            //实际支付
            var payment = total - discount + postage;

            //检查钱包余额是否足够
            if (input.PayMode == EnumPayMode.BALANCE || input.PayMode == EnumPayMode.BONUS)
            {
                var walletResponse = await _userRpcService.GetUsrWallet();
                if (!walletResponse.IsSuccessStatusCode)
                    return Problem(HttpStatusCode.InternalServerError, "系统错误");
                var wallet = walletResponse.Content;

                if ((input.PayMode == EnumPayMode.BALANCE && wallet.Balance < payment) || (input.PayMode == EnumPayMode.BONUS && wallet.ShoppingMoney < payment))
                    return Problem(HttpStatusCode.BadRequest, "余额不足");
            }

            Order order = new Order()
            {
                Id = IdGenerater.GetNextId(),
                UserId = userid,
                Code = this.CreateNo(),
                ConsigneeName = userAddress?.Name,
                ConsigneePhone = userAddress?.Tele,
                ConsigneeProvince = userAddress?.ProvinceName,
                ConsigneeCity = userAddress?.CityName,
                ConsigneeAreas = userAddress?.AreaName,
                ConsigneeStreet = userAddress?.StreetName,
                Address = userAddress?.Address,
                Total = total,
                Postage = postage,
                Discount = discount,
                Payment = payment,
                State = 1,
                CreateTime = DateTime.Now,
                PayMode = (int)input.PayMode,
                PayApp = input.PayApp,
                BuyerMessage = input.BuyerMessage,
                Account = userinfor.Account,
                Reference = input.Reference,
                ImagesAddress = userAddress == null ? null: JsonSerializer.Serialize(userAddress),
                Warehouse = input.Type == 3,
                WarehouseType = 0
            };

            if (input.Type == 4)
                order.OrderType = (int)EnumOrderType.TAKE_HOME;
            else
                order.OrderType = (int)EnumOrderType.NORMAL;

            var goods = new List<OrderGoods>();
            var orderGift = new List<OrderGiftDto>();
            foreach (var item in orderGoods)
            {
                var size = orderProduct.First(c => c.SizeId == item.SizeId);
                var ordergoods = new OrderGoods()
                {
                    Id = IdGenerater.GetNextId(),
                    OrderId = order.Id,
                    UserId = userid,
                    GoodsId = size.GoodsId,
                    GoodsName = size.GoodsName,
                    GoodsSizeId = size.SizeId,
                    GoodsSizeName = size.SizeName,
                    IsThirdParties = size.IsThirdParties,
                    GoodsType = size.GoodsType,
                    GoodsImage = size.Thumbnail,
                    Price = item.Price,
                    Quantity = item.Count,
                    TotalPrice = item.Total,
                    IsGift = false,
                    ImagesGoods = JsonSerializer.Serialize(size)
                };
                goods.Add(ordergoods);

                //计算赠品
                if (input.Type != 3 && input.Type != 4 && size.Gift.Count > 0)
                {
                    foreach (var gift in size.Gift)
                    {
                        var num = 0;
                        if (gift.IsSuperposition)
                            num = ordergoods.Quantity / gift.BuyCount * gift.Count;
                        else
                            num = ordergoods.Quantity >= gift.BuyCount ? gift.Count : 0;
                        num = num > gift.RemainderCount ? gift.RemainderCount : num;
                        if (num > 0)
                        {
                            orderGift.Add(new OrderGiftDto
                            {
                                GiftId = gift.Id,
                                GoodId = item.GoodsId,
                                SizeId = item.SizeId,
                                GiftGoodsId = gift.GoodsId,
                                GiftSizeId = item.SizeId,
                                Num = num,
                            });
                            var detail = gift.GiftDetail;
                            goods.Add(new OrderGoods()
                            {
                                Id = IdGenerater.GetNextId(),
                                OrderId = order.Id,
                                GoodsId = detail.GoodsId,
                                GoodsName = detail.GoodsName,
                                GoodsSizeId = detail.SizeId,
                                GoodsSizeName = detail.SizeName,
                                GoodsType = detail.GoodsType,
                                IsThirdParties = detail.IsThirdParties,
                                GoodsImage = detail.Thumbnail,
                                Quantity = num,
                                IsGift = true,
                                ImagesGoods = JsonSerializer.Serialize(detail),
                                GiftId = gift.Id,
                                ParentOrderGoodsId = ordergoods.Id
                            });
                        }
                    }
                }
            }

            if(DeductedGoods > 0)
                goods.First(x => x.GoodsSizeId == DeductedGoods).UseDeductionCoupons = true;

            List<OrderCoupon> orderCoupons = new List<OrderCoupon>();
            input.CouponIds.ForEach(x => orderCoupons.Add(new OrderCoupon {
                Id = IdGenerater.GetNextId(),
                UserCouponId = x,
                OrderId = order.Id,
            }));

            await _orderCouponRepository.InsertRangeAsync(orderCoupons);
               

            //创建支付单
            var payResponse = await _payRpcService.OrderPayCreate(new Adnc.Shared.RpcServices.Rtos.Pay.OrderPayCreateRto() { 
                OrderId = order.Id,
                Amount = order.Payment,
                PaidMode = order.PayMode
            });
            if(!payResponse.IsSuccessStatusCode)
                return Problem(HttpStatusCode.InternalServerError, "系统错误");

            //发布商品库存修改事件
            var proEventId = IdGenerater.GetNextId();
            var proEventData = new ProductInventoryChangeEvent.EventData() {
                Type = 1,
                Product = orderGoods.Select(x => new ProductInventoryChangeEvent.Change { Id = x.SizeId,Num = x.Count}),
                Gift = orderGift.Select(x => new ProductInventoryChangeEvent.Change { Id = x.GiftId,Num = x.Num }),
            };
            var proEventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
            await _eventPublisher.PublishAsync(new ProductInventoryChangeEvent(proEventId, proEventData, proEventSource));


            //发布支付事件
            var eventId = IdGenerater.GetNextId();
            var eventData = new OrderPaidEvent.EventData() { OrderId = order.Id, UserId = userid, Amount = payment, PaidMode = (int)input.PayMode };
            var eventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
            await _eventPublisher.PublishAsync(new OrderPaidEvent(eventId, eventData, eventSource));

            await _orderRepository.InsertAsync(order);
            await _orderGoodsRepository.InsertRangeAsync(goods);

            //删除购物车数据
            if (input.IsShoppingCart)
                await _shoppingCartRepository.DeleteRangeAsync(c => c.UserId.Equals(userid) && sizeIds.Contains(c.SizeId));

            return order.Id;
        }

        /// <summary>
        /// 获取订单详情
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<OrderDetailDto>> GetOrderDetail(long orderId)
        {
            var order = await _orderRepository.FindAsync(orderId);
            if (order == null)
                return Problem(HttpStatusCode.NotFound);
            var goods = await _orderGoodsRepository.Where(c => c.OrderId == order.Id)
                .Select(c => new OrderGoodsDetailDto
                {
                    OrderGoodsId = c.Id,
                    GoodsId = c.GoodsId,
                    GoodsName = c.GoodsName,
                    GoodsSizeId = c.GoodsSizeId,
                    GoodsSizeName = c.GoodsSizeName,
                    GoodsImage = c.GoodsImage,
                    Quantity = c.Quantity,
                    Price = c.Price,
                    TotalPrice = c.TotalPrice,
                    ImagesGoods = c.ImagesGoods,
                    UseDeductionCoupons = c.UseDeductionCoupons,
                    IsGift = c.IsGift,
                }).ToListAsync();

            var orderDetail = Mapper.Map<OrderDetailDto>(order);
            orderDetail.OrderId = order.Id;
            orderDetail.OrderGoods = goods;
            return orderDetail;
        }

        /// <summary>
        /// 修改订单状态
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult> ChangeOrderState(OrderStateModifyDto input)
        {
            var order = await _orderRepository.FindAsync(input.OrderId);
            if (order != null)
            {
                if (input.ChangeType == 1 && order.State < 2)
                {
                    order.State = 2;
                    order.PaymentTime = input.ChangeTime;
                    await _orderRepository.UpdateAsync(order, UpdatingProps<Order>(c => c.State, c => c.PaymentTime));

                    var coupon = await _orderCouponRepository.Where(x => x.OrderId == order.Id).Select(x => x.UserCouponId).ToListAsync();
                    if (coupon.Any())
                        await _userCouponRepository.UpdateRangeAsync(x => coupon.Contains(x.Id), x => new UserCoupon { State = 3 });

                    //发布出库信息
                    var eventId = IdGenerater.GetNextId();
                    var eventData = new OrderExwarehouseEvent.EventData() { OrderId = order.Id };
                    var eventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
                    await _eventPublisher.PublishAsync(new OrderExwarehouseEvent(eventId, eventData, eventSource));
                }
                else if (input.ChangeType == 2 && order.State < 2)
                {
                    order.State = -1;
                    order.CloseTime = input.ChangeTime;
                    await _orderRepository.UpdateAsync(order, UpdatingProps<Order>(c => c.State, c => c.PaymentTime));

                    var coupon = await _orderCouponRepository.Where(x => x.OrderId == order.Id).Select(x => x.UserCouponId).ToListAsync();
                    if (coupon.Any())
                        await _userCouponRepository.UpdateRangeAsync(x => coupon.Contains(x.Id), x => new UserCoupon { State = 1 });

                    var orderGoods = await _orderGoodsRepository.Where(x => x.OrderId == order.Id).ToListAsync();
                    //发布商品库存修改事件
                    var proEventId = IdGenerater.GetNextId();
                    var proEventData = new ProductInventoryChangeEvent.EventData()
                    {
                        Type = -1,
                        Product = orderGoods.FindAll(x => x.IsGift == false).Select(x => new ProductInventoryChangeEvent.Change { Id = x.GoodsSizeId, Num = x.Quantity }),
                        Gift = orderGoods.FindAll(x => x.IsGift == true).Select(x => new ProductInventoryChangeEvent.Change { Id = x.GiftId.Value, Num = x.Quantity }),
                    };
                    var proEventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
                    await _eventPublisher.PublishAsync(new ProductInventoryChangeEvent(proEventId, proEventData, proEventSource));
                }
                return AppSrvResult();
            }
            else
                throw new Exception("找不到相应订单");
        }

        /// <summary>
        /// 订单列表查询
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<PageModelDto<OrderListDto>>> OrderListSearch(long userid, SearchPagedDto<int> input)
        {
            var list = new List<OrderListDto>();
            var expression = ExpressionCreator.New<Order>().And(x => x.UserId == userid)
                .AndIf(input.Value > 0, x => x.State == input.Value);
            int count = await _orderRepository.CountAsync(expression);
            List<Order> orders = _orderRepository.Where(expression)
                    .OrderByDescending(c => c.CreateTime)
                    .Skip(input.PageSize * (input.PageIndex - 1))
                    .Take(input.PageSize)
                    .ToList();

            if(orders.Count > 0)
            {
                var ids = orders.Select(c => c.Id).ToList();
                var product = _orderGoodsRepository.Where(c => ids.Contains(c.OrderId)).ToList();
                foreach (var item in orders)
                {
                    var order = new OrderListDto() { 
                        Id = item.Id,
                        Code = item.Code,
                        State = item.State,
                        Postage = item.Postage,
                        Payment = item.Payment,
                    };
                    order.Prodcut = product.FindAll(c => c.OrderId == item.Id).Select(c => new OrderListProdcutDto()
                    {
                        Id = c.Id,
                        GoodsId = c.GoodsId,
                        GoodsName = c.GoodsName,
                        GoodsSizeId = c.GoodsSizeId,
                        GoodsSizeName = c.GoodsSizeName,
                        GoodsImage = c.GoodsImage,
                        State = c.State,
                        Price = c.Price,
                        IsGift = c.IsGift,
                        Quantity = c.Quantity,
                    }).ToList();
                    order.Count = order.Prodcut.Sum(c => c.Quantity);
                    list.Add(order);
                }
            }
            return new PageModelDto<OrderListDto>(input.PageIndex, input.PageSize,list, count);
        }

        /// <summary>
        /// 订单同步到ERP
        /// </summary>
        /// <param name="orInfo"></param>
        /// <param name="goods"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private AppSrvResult SetOrderToERP(Order orInfo, List<OrderGoodsDto> goods)
        {            
            var data = new
            {
                shop_id = int.Parse(_erpOptions.Value.StoreCode), //店铺编号
                so_id = orInfo.Code, //线上订单号, 长度 <= 50 
                order_date = orInfo.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), //订单日期(2018-09-01 23:59:11 )                                                                               
                shop_status = "WAIT_SELLER_SEND_GOODS", //订单状态: WAIT_BUYER_PAY：等待买家付款， WAIT_SELLER_SEND_GOODS:等待卖家发货,WAIT_BUYER_CONFIRM_GOODS:等待买家确认收货, TRADE_FINISHED:交易成功, TRADE_CLOSED:付款后交易关闭,TRADE_CLOSED_BY_TAOBAO:付款前交易关闭 
                shop_buyer_id = orInfo.UserId.ToString(), //买家帐号(暂存用户昵称) 长度 <= 50 
                receiver_state = orInfo.ConsigneeProvince, //收货省份 长度 <= 50 
                receiver_city = orInfo.ConsigneeCity, //收货市 长度<=50 
                receiver_district = orInfo.ConsigneeAreas, //收货区/街道 长度<=50 
                receiver_address = $"{orInfo.ConsigneeStreet} {orInfo.Address}", //收货地址 长度<=200
                receiver_name = orInfo.ConsigneeName, //收件人 长度<=50 
                receiver_phone = orInfo.ConsigneePhone, //联系电话 长度<=50 
                pay_amount = orInfo.Payment, //应付金额，保留两位小数，单位元） 
                freight = orInfo.Postage, //运费 
                shop_modified = orInfo.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"), //订单修改日期 
                items = new List<object>(), //商品明细 
                pay = new
                {
                    outer_pay_id = orInfo.Code, //外部支付单号，最大50 
                    pay_date = orInfo.PaymentTime.HasValue ? orInfo.PaymentTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : DateTime.Now.ToString(), //支付日期 
                    payment = orInfo.PayMode == 1 ? "微信" : "余额支付", //支付方式，最大20 
                    seller_account = "官方", //卖家支付账号，最大 50 
                    buyer_account = orInfo.UserId.ToString(), //买家支付账号，最大 200 
                    amount = orInfo.Payment //支付金额 
                }, 
            };

            foreach (var item in goods)
            {
                if (!item.ProductSizeDetailDto.IsThirdParties || item.ProductSizeDetailDto.ERPEnable)
                {

                    data.items.Add(new
                    {
                        sku_id = item.ProductSizeDetailDto.ERPCode, //商品编码 长度<=40
                        shop_sku_id = item.GoodsSizeId.ToString(), //店铺商品编码 长度<=128 
                        pic = $"https://oss.wyuntao.com/{item.ProductSizeDetailDto.Thumbnail}", //图片地址 长度<=300 
                        properties_value = item.GoodsSizeName, //商品属性；长度<=100 
                        amount = item.TotalPrice, //应付金额，保留两位小数，单位（元）；备注：可能存在人工改价 
                        base_price = item.Price, //基本价（拍下价格），保留两位小数，单位（元） 
                        qty = item.Quantity, //数量 
                        name = item.GoodsName, //商品名称 长度<=100 
                        outer_oi_id = item.Id.ToString(), //商家系统订单商品明细主键,最长不超过 50,保持唯一 
                    });
                }
            }
            if (data.items.Count >= 1)
            {
                ERPHelper erp = new ERPHelper(_erpOptions.Value);
                var obj = erp.Post<Adnc.Application.Shared.ReturnData>("jushuitan.orders.upload", new List<object> { data });
                if (obj.code != 0)
                    throw new Exception($"ERP调用错误：{data.so_id}");
            }

            return AppSrvResult();
        }

        /// <summary>
        /// 出库完成后的处理
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task<AppSrvResult> TasksAfterExwarehouseComplete(long orderId)
        {
            var order = await _orderRepository.FindAsync(orderId);
            var orderGoods = await _orderGoodsRepository.Where(c => c.OrderId == orderId).ToListAsync();
            List<OrderGoodsDto> goods = new List<OrderGoodsDto>();
            orderGoods.ForEach(x =>
            {
                var deteail = Mapper.Map<OrderGoodsDto>(x);
                deteail.ProductSizeDetailDto = JsonSerializer.Deserialize<ProductSizeDetailDto>(x.ImagesGoods);
                goods.Add(deteail);
            });

            //云仓订单
            if (order.Warehouse)
            {
                order.State = 4;
                await _orderRepository.UpdateAsync(order, UpdatingProps<Order>(x => x.State));
                await _orderGoodsRepository.UpdateRangeAsync(x => x.OrderId == order.Id, c =>  new OrderGoods { State = 2 });
                return AppSrvResult();
            }

            SetOrderToERP(order,goods);
            await CouponIssuance(order, goods);
            return AppSrvResult();
        }

        /// <summary>
        /// 发放优惠券
        /// </summary>
        /// <param name="order"></param>
        /// <param name="goods"></param>
        /// <returns></returns>
        public async Task<AppSrvResult> CouponIssuance(Order order, List<OrderGoodsDto> goods)
        {
            //赠送体验券
            if (goods.Any(x => x.ProductSizeDetailDto.GoodsType == (int)EnumGoodsType.GOODS_TYPE_SAMPLE))
            {
                List<UserCoupon> coupons = new List<UserCoupon>();
                //判断是否是第一次购买 如果是就送40元的优惠券
                var exite = await _orderRepository.AnyAsync(x => x.Id != order.Id && x.UserId == order.UserId);
                if (!exite)
                {
                    coupons.Add(new UserCoupon()
                    {
                        Id = IdGenerater.GetNextId(),
                        CouponId = CashCoupon,
                        StartTime = DateTime.Now.Date,
                        EndTime = DateTime.Now.Date.AddMonths(3),
                        GetTime = DateTime.Now,
                        Type = (int)EnumCouponType.MANJIAN,
                        State = 1,
                        UserId = order.UserId,
                    });
                }

                //判断是否购买或者兑换过体验装 没有就送36个月的体验装兑换券
                var Purchased = await _orderGoodsRepository.AnyAsync(x => x.UserId == order.UserId && x.GoodsType == (int)EnumGoodsType.GOODS_TYPE_SAMPLE && x.OrderId != order.Id);
                if (!Purchased)
                {
                    var StartTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);

                    for (var i = 0; i < 36; i++)
                    {
                        coupons.Add(new UserCoupon()
                        {
                            Id = IdGenerater.GetNextId(),
                            CouponId = ExperienceCoupon,
                            EndTime = StartTime.AddMonths(1),
                            StartTime = StartTime,
                            GetTime = DateTime.Now,
                            Type = (int)EnumCouponType.DIKOU,
                            State = 1,
                            UserId = order.UserId,
                        });
                        StartTime = StartTime.AddMonths(1);
                    }
                }

                if (coupons.Count > 0)
                    await _userCouponRepository.InsertRangeAsync(coupons);
            }

            return AppSrvResult();
        }


        /// <summary>
        /// ERP回调
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult> ErpCallBack(ERPCallbackDto input)
        {
            await _messageLogRepository.AddAsync(new MessageLog
            {
                LogType = "ERP回调",
                Message = JsonSerializer.Serialize(input),
                Describe = $"订单号：{input.o_id}",
                Time = DateTime.Now
            });
            var orderParce = await _orderParcelRepository.FindAsync(x => x.ExpressNumber == input.l_id);
            if (orderParce == null)
            {
                orderParce = new OrderParcel()
                {
                    Id = IdGenerater.GetNextId(),
                    ExpressNumber = input.l_id,
                    CourierCompany = input.logistics_company,
                    CourierCompanyType = input.lc_id,
                    ShipmentsTime = input.send_date,
                    IsReturn = false,
                };
                await _orderParcelRepository.InsertAsync(orderParce);
            }
            var orderParcelGoods = new List<OrderParcelGoods>();

            var orders = await _orderRepository.FindAsync(x => x.Code == input.so_id);
            var ordergoods = await _orderGoodsRepository.Where(x => x.OrderId == orders.Id).ToListAsync();
            var changeOrderGoodsList = new List<OrderGoods>();
            foreach (var item in input.items)
            {
                var cg = ordergoods.FirstOrDefault(x => x.Id == long.Parse(input.items[0].outer_oi_id));
                cg.State = 1;
                cg.SendTime = input.send_date;
                changeOrderGoodsList.Add(cg);
                orderParcelGoods.Add(new OrderParcelGoods()
                {
                    Id = IdGenerater.GetNextId(),
                    OrderId = cg.OrderId,
                    OrdGoodsId = cg.Id,
                    OrdParcel = orderParce.Id,
                    Quantity = item.qty,
                });
            }
            if (orders.State == 2)
            {
                orders.State = 3;
                orders.SendTime = input.send_date;
                await _orderRepository.UpdateAsync(orders, UpdatingProps<Order>(x => x.State));
            }              

            await _orderParcelGoodsRepository.InsertRangeAsync(orderParcelGoods);
            await _orderGoodsRepository.UpdateRangeAsync(changeOrderGoodsList, UpdatingProps<OrderGoods>(x => x.State, x => x.SendTime));
            //给用户发送消息
            var proEventId = IdGenerater.GetNextId();
            var proEventData = new UserGZHMessagePushEvent.EventData()
            {
                UserId = orders.UserId,
                Pagepath = "pages/wel",
                TemplateId = "xUPx3Ro4Rn2YHX3VUvcfyvDsjmYcsQer2X1T_bAPYww",
                Data = new
                {
                    first = new
                    {
                        value = "亲，宝贝已经启程了，好想快点来到你身边！",
                        color = "#173177"
                    },
                    keyword1 = new
                    {
                        value = input.so_id,
                        color = "#173177"
                    },
                    keyword2 = new
                    {
                        value = orderParce.CourierCompany,
                        color = "#173177"
                    },
                    keyword3 = new
                    {
                        value = orderParce.ExpressNumber,
                        color = "#173177"
                    },
                    remark = new
                    {
                        value = "点击小程序进入，查看详情。",
                        color = "#173177"
                    }
                }
            };
            var proEventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
            await _eventPublisher.PublishAsync(new UserGZHMessagePushEvent(proEventId, proEventData, proEventSource));

            return AppSrvResult();
        }

        /// <summary>
        /// 物流追踪
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult<List<LogisticsTrackingDto>>> LogisticsTracking(long input)
        {
            List<LogisticsTrackingDto> result = new List<LogisticsTrackingDto>();
            var orderParceGoods = await _orderParcelGoodsRepository.Where(x => x.OrdGoodsId == input).ToListAsync();
            if (orderParceGoods.Any())
            {
                var orderParceIds = orderParceGoods.Select(x => x.OrdParcel).ToList();
                var orderParce = _orderParcelRepository.Where(x => orderParceIds.Contains(x.Id)).ToList();
                foreach (var item in orderParce)
                {
                    String url = "https://wuliu.market.alicloudapi.com" + "/kdi?no=" + item.ExpressNumber;
                    string appCode = "ac5b638410aa43c8b1f1c43868b55ef3";
                    var Request = WebRequest.CreateDefault(new Uri(url));
                    Request.Method = "get";
                    Request.Headers.Add("Authorization", "APPCODE " + appCode);
                    var Response = await Request.GetResponseAsync();
                    Stream st = Response.GetResponseStream();
                    StreamReader reader = new StreamReader(st, Encoding.GetEncoding("utf-8"));

                    var data = JsonSerializer.Deserialize<LogisticsTrackingResultDto>(reader.ReadToEnd(), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
                    if (data.Msg.Equals("ok"))
                    {
                        data.Result.Quantity = orderParceGoods.First(x => x.OrdParcel == item.Id).Quantity;
                        result.Add(data.Result);
                    }                        
                }
            }

            return result;
        }

        /// <summary>
        /// 收货确认
        /// </summary>
        /// <param name="input">订单商品ID</param>
        /// <returns></returns>
        public async Task<AppSrvResult> ReceiptConfirmation(long input)
        {
            var ordergoods = await _orderGoodsRepository.FindAsync(input);
            if (ordergoods == null)
                return Problem(null, "参数错误");
            if (ordergoods.State != 1)
                return Problem(null, "商品未发货或者已收货");
            ordergoods.State = 2;
            ordergoods.ConfirmTime = DateTime.Now;
            await _orderGoodsRepository.UpdateAsync(ordergoods, UpdatingProps<OrderGoods>(x => x.State, x => x.ConfirmTime));
            var exit = await _orderGoodsRepository.AnyAsync(x => x.OrderId == ordergoods.OrderId && (x.State == 0 || x.State == 1) && x.Id != ordergoods.Id);
            if(!exit)
            {
                await _orderRepository.UpdateAsync(new Order { Id = ordergoods.OrderId, State = 4 }, UpdatingProps<Order>(x => x.State));
                //发布订单收货完成事件
                var eventId = IdGenerater.GetNextId();
                var eventData = new OrderReceivingEvent.EventData() { OrderId = ordergoods.OrderId };
                var eventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
                await _eventPublisher.PublishAsync(new OrderReceivingEvent(eventId, eventData, eventSource));
            }
                
            return AppSrvResult();
        }

        /// <summary>
        /// 添加评论
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppSrvResult> AddReview(long userId, GoodsReviewAddDto input)
        {
            var orderGoods = await _orderGoodsRepository.FindAsync(input.Id);
            if (orderGoods == null)
                return Problem(null, "参数错误");

            var reviewRepone = await _productRPCService.ProductAddReview(new ProductAddReviewRto
            {
                UserId = userId,
                GoodsId = orderGoods.GoodsId,
                Content = input.Content,
                Image = JsonSerializer.Serialize(input.Image),
                Video = JsonSerializer.Serialize(input.Video),
                GoodGrade = input.GoodGrade,
                Logistics = input.Logistics,
                OrderId = orderGoods.OrderId,
                Serve = input.Serve,
                Service = input.Service,
            });

            if (!reviewRepone.IsSuccessStatusCode)
                return Problem(HttpStatusCode.InternalServerError, "服务异常");
            orderGoods.State = 3;
            await _orderGoodsRepository.UpdateAsync(orderGoods, UpdatingProps<OrderGoods>(y => y.State));
            return AppSrvResult();
        }

        /// <summary>
        /// 订单检查 发货时间超过14但未收货的订单默认为已收货
        /// </summary>
        /// <returns></returns>
        public async Task<AppSrvResult> OrderCheck()
        {
            var orders = await _orderRepository.Where(x => x.SendTime <= DateTime.Now.AddDays(-14) && x.State == 3).ToListAsync();
            foreach (var item in orders)
            {
                var goods = await _orderGoodsRepository.Where(x => x.OrderId == item.Id).ToListAsync();
                if (!goods.Any(x => x.State == 0))
                {
                    item.State = 4;
                    item.ConfirmTime = DateTime.Now;

                    await _orderRepository.UpdateAsync(item, UpdatingProps<Order>(x => x.State,x=> x.ConfirmTime));
                    await _orderGoodsRepository.UpdateRangeAsync(x => x.OrderId == item.Id && x.State == 1, y => new OrderGoods { State = 2, ConfirmTime = DateTime.Now });

                    //发布订单收货完成事件
                    var eventId = IdGenerater.GetNextId();
                    var eventData = new OrderReceivingEvent.EventData() { OrderId = item.Id };
                    var eventSource = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
                    await _eventPublisher.PublishAsync(new OrderReceivingEvent(eventId, eventData, eventSource));
                }
            }

            return AppSrvResult();
        }

        private string CreateNo()
        {
            Random random = new Random();
            string strRandom = random.Next(10000, 99999).ToString();
            string code = $"DD{DateTime.Now.ToString("yyyyMMddHHmmssffff")}{strRandom}";
            return code;
        }
    }
}
